/* HebSupport - Hebrew support for iPhone
 * Copyright (C) 2009 Aviram Segal (planB) Liraz Elias (Liraz) and Omri Gilad (mmgm)
*/

/* 
 * This program is free software; you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation; either version 2 of the License, or 
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
 * for more details.
 * 
 * You should have received a copy of the GNU General Public License along 
 * with this program; if not, write to the Free Software Foundation, Inc., 
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#import <Foundation/Foundation.h>
#import "RegexKitLite.h"
#include "substrate.h"
#include "iVrit.h"
#include <alloca.h>
#include <unistd.h>
#include "ABMemberCell.h"
#include <execinfo.h>

/* Declare ICU functions */
extern "C" void* ubidi_open();
extern "C" void ubidi_close(void*);
extern "C" void ubidi_setPara( void*, UChar*, int, UBiDiLevel, void*, UErrorCode* );
extern "C" int32_t ubidi_writeReordered( void*, UChar*, int, uint16_t, UErrorCode* );
extern "C" void ubidi_setReorderingMode(void*, UBiDiReorderingMode);
extern "C" UBiDiDirection ubidi_getDirection(void*);
extern "C" UBiDiLevel ubidi_getLevelAt(void*, int32_t);
extern "C" UBiDiLevel ubidi_getParaLevel(void*);
extern "C" UBool u_isalpha (UChar);

#define WEBCORE_FRAMEWORK "/System/Library/PrivateFrameworks/WebCore.framework/WebCore"
#define LIBICUCORE "/usr/lib/libicucore.dylib"

/* Bidi */
FloatSize (*__orig__Font$drawText)(void* _this, void* context, TextRun& run, FloatPoint& point, int from, int to) = 0x0;

FloatSize __Font$drawText(void* _this, void* context, TextRun& run, FloatPoint& point, int from, int to) {
    void *bidi;
    UErrorCode err = U_ZERO_ERROR;
    UBiDiLevel level = UBIDI_DEFAULT_LTR;

    bidi=ubidi_open();

    ubidi_setReorderingMode(bidi, UBIDI_REORDER_NUMBERS_SPECIAL);

    UChar* input = (UChar*)run.m_characters;
    int len = run.m_len;
    ubidi_setPara( bidi, input, len, level, NULL, &err );
    TextRun newRun = run;
    UChar* output = (UChar*)alloca(len * sizeof(UChar));
	UBiDiDirection dir;
	
	//NSLog(@"Inside hooked Font$drawText, len=%d", len);
	
	dir = ubidi_getDirection(bidi);
	
	if(dir == UBIDI_MIXED){
		level = ubidi_getParaLevel(bidi);
		if(level % 2 == 0){
			ubidi_writeReordered( bidi, output, len, UBIDI_DO_MIRRORING, &err );
			newRun.m_characters = output;
		} else {
			ubidi_writeReordered( bidi, output, len, UBIDI_OUTPUT_REVERSE, &err );
			newRun.m_characters = output;
			newRun.m_rtl = true;
		}
	} else if(!(run.m_rtl) && (dir == UBIDI_RTL)){
		ubidi_writeReordered( bidi, output, len, UBIDI_OUTPUT_REVERSE, &err );
		newRun.m_characters = output;
		newRun.m_rtl = true;
	}
 
    FloatSize ret =  __orig__Font$drawText(_this, context, newRun, point, from, to);
    ubidi_close(bidi);
    return ret;
}

static id ABMembersController$sectionIndexTitlesForTableView(id self, SEL sel, id tableView) {
	NSInteger numOfSections = [self numberOfSectionsInTableView:tableView];
	NSMutableArray* mutableTitles = [NSMutableArray arrayWithCapacity:(numOfSections+1)];
	[mutableTitles insertObject:@"{search}" atIndex:0];
	NSUInteger i;
	for (i = 0; i < numOfSections; i++) {
		NSObject* obj = [self tableView:tableView titleForHeaderInSection:i];
		if(obj != nil) {
				[mutableTitles insertObject:obj atIndex:(i+1)];
		}
	}
	return mutableTitles;
}

static void ABMemberCell$drawContentInRectSelected(id self, SEL sel, CGRect rect, BOOL selected) {
	ABMemberCell* _self = (ABMemberCell*)self;
	NSArray* origNamePieces = _self->_namePieces;
	// If empty array we can stop
	if([origNamePieces count] == 0) {
		[self orig$drawContentInRect:rect selected:selected];
		return;
	}
	
	NSMutableArray* namePieces = [NSMutableArray arrayWithArray:origNamePieces];
	BOOL isHebrew = false;
	int origHighlightIndex = _self->_highlightIndex;
	
	NSString* firstName = [namePieces objectAtIndex:0];
	unichar firstNameFirstChar = [firstName characterAtIndex:0];
	if(0x0590 <= firstNameFirstChar && firstNameFirstChar <= 0x05FF) {
		isHebrew = true;
	}
	
	if(isHebrew) {
		int count = [namePieces count];
		if(count == 2) {
			[namePieces exchangeObjectAtIndex:0 withObjectAtIndex:1];
			_self->_highlightIndex = (origHighlightIndex+1)%2;
		} else if(count ==3) {
			// Person with prefix such as Doctor
			[namePieces exchangeObjectAtIndex:0 withObjectAtIndex:2];
			if(_self->_highlightIndex != 1) {
				_self->_highlightIndex = (origHighlightIndex+2)%4;
			}
		}
		_self->_namePieces = namePieces;
	}
	[self orig$drawContentInRect:rect selected:selected];
	_self->_highlightIndex = origHighlightIndex;
	_self->_namePieces = origNamePieces;
}

void dumpSubviews(UIView *view, int level){
	NSMutableString *padding = [[NSMutableString alloc] init];
	for(int i=0; i<level; i++){
		[padding appendString:@"\t"];
	}
	NSLog(@"%@%@", padding, [view class]);
	[padding dealloc];
	NSArray *subviews = [view subviews];
	NSInteger count;
	if((count = [subviews count]) == 0){
		return;
	} else {
		level++;
		for(int i=0; i<count; i++){
			dumpSubviews([subviews objectAtIndex:i], level);
		}
		return;
	}
}

void setText(id self, SEL sel, NSString *text){
	void *bidi;
    UErrorCode err = U_ZERO_ERROR;
    UBiDiLevel level = UBIDI_DEFAULT_LTR;
	UBiDiDirection dir;
	
	/*if([text compare:@"dumpit"] == 0){
		dumpSubviews([[UIApplication sharedApplication] keyWindow], 0);
		return;
	}*/
	
	int len = [text length];
    bidi=ubidi_open();
	UChar *input = (UChar *)calloc(len, sizeof(UChar));
	[text getCharacters:(unichar *)input];
    ubidi_setPara( bidi, (UChar*)input, len, level, NULL, &err );
	dir = ubidi_getDirection(bidi);
	Class cls = object_getClass(self);
	//NSLog(@"Inside hooked [%@ setText:], text=%@, dir=%s", cls, text, dir==UBIDI_LTR?"LTR":"RTL/Mixed");
	if(dir == UBIDI_RTL){
		[self setTextAlignment:UITextAlignmentRight];
	} else if (dir == UBIDI_MIXED) {
		level = ubidi_getParaLevel(bidi);
		if(level % 2 == 1) {
			[self setTextAlignment:UITextAlignmentRight];
		}
		else {
			[self setTextAlignment:UITextAlignmentLeft];
		}
	}
	else {
		[self setTextAlignment:UITextAlignmentLeft];
	}
	[self orig$setText:text];
	ubidi_close(bidi);
	free(input);
}

void setTextAlignment(id self, SEL selector, UITextAlignment align) {
	Class cls = [self class];
	//NSLog(@"Inside hooked [%@ setTextAlignment:]", cls);
	[self orig$setTextAlignment:align];
	Ivar var = class_getInstanceVariable(cls, "_style");
	NSString *string = [self _style];
	if(string == nil){
		//NSLog(@"Failed to load style, aborting direction mods");
		return;
	}
	NSMutableString *newStyle = [[NSMutableString alloc] initWithString:string];
	[string release];
	NSString *newDir = [[NSString alloc] initWithFormat:@"%s", align==UITextAlignmentLeft?"ltr":"rtl"];
	//TODO: Perform this without regex. I know its a waste of cycles on a device like the iPhone, but I <3 regex too much.
	if([newStyle replaceOccurrencesOfRegex:@"direction\: (.){3}" withString:[NSString stringWithFormat:@"direction: %@", newDir]] == 0){
		[newStyle appendFormat:@"direction: %@;", newDir];
	}
	[newDir dealloc];
	object_setIvar(self, var, newStyle);
}

NSString *styleString(id self, SEL selector){
	UITextAlignment align = [self textAlignment];
	NSString *newDir = [[NSString alloc] initWithFormat:@"%s", align==UITextAlignmentLeft?"ltr":"rtl"];
	NSString *newAlign = [[NSString alloc] initWithFormat:@"%s", align==UITextAlignmentLeft?"left":"right"];
	NSMutableString *newStyle = [NSMutableString stringWithString:[self orig$styleString]];
	NSLog(@"Original style: %@", newStyle);
	if([newStyle replaceOccurrencesOfRegex:@"text\-align\: (.){4,5};" withString:[NSString stringWithFormat:@"text-align: %@;", newAlign]] == 0){
		[newStyle appendFormat:@"text-align: %@;", newAlign];
	}
	if([newStyle replaceOccurrencesOfRegex:@"direction\: (.){3}" withString:[NSString stringWithFormat:@"direction: %@", newDir]] == 0){
		[newStyle appendFormat:@"direction: %@;", newDir];
	}
	[newDir dealloc];
	[newAlign dealloc];
	NSLog(@"Returning style: %@", newStyle);
	return newStyle;
}

void setContentToHTMLString(id self, SEL selector, NSString *html){
	NSMutableString *text = [[NSMutableString alloc] initWithString:html];
	[text replaceOccurrencesOfRegex:@"<(\/)?div>" withString:@""];
	
	void *bidi;
    UErrorCode err = U_ZERO_ERROR;
    UBiDiLevel level = UBIDI_DEFAULT_LTR;
	UBiDiDirection dir;
	
	int len = [text length];
    bidi=ubidi_open();
	UChar *input = (UChar *)calloc(len, sizeof(UChar));
	[text getCharacters:(unichar *)input];
    ubidi_setPara( bidi, (UChar*)input, len, level, NULL, &err );
	dir = ubidi_getDirection(bidi);
	static UITextAlignment align;
	if(dir == UBIDI_RTL){
		align = UITextAlignmentRight;
	} else if (dir == UBIDI_MIXED) {
		level = ubidi_getParaLevel(bidi);
		if(level % 2 == 1) {
			align = UITextAlignmentRight;
		}
		else {
			align = UITextAlignmentLeft;
		}
	}
	else {
		align = UITextAlignmentLeft;
	}
	[self setTextAlignment:align];
	ubidi_close(bidi);
	free(input);
	[text release];
	[self orig$setContentToHTMLString:html];
}

extern "C" void HSInitialize() {
	//freopen([@"/tmp/ivrit_log.txt" fileSystemRepresentation], "a", stderr);

	/* BiDi supprt  */
	struct nlist nl[4];
	int res = 0;
	memset(nl, 0x0, sizeof(nl));
	/* Hooks to WebCore/platform/graphics/Font.h - Font::drawText(GraphicsContext*, TextRun&, FloatPoint&, int from, int to); */
	nl[0].n_un.n_name = (char *) "__ZNK7WebCore4Font8drawTextEPNS_15GraphicsContextERKNS_7TextRunERKNS_10FloatPointEii";
	if((res = nlist(WEBCORE_FRAMEWORK, nl)) != -1) {
		MSHookFunction((void *)nl[0].n_value, (void *)&__Font$drawText, (void **)&__orig__Font$drawText);
		NSLog(@"Finished hooking funcs, res=%d", res);
	} else {
		NSLog(@"Failed to hook drawText, res=%d", res);
	}
  
	// We need this lib for Bidi
	if (dlopen(LIBICUCORE, RTLD_LAZY) == NULL) {
		NSLog(@"Failed to load icu");
	}
	MSHookMessage(objc_getClass("ABMembersController"), @selector(sectionIndexTitlesForTableView:), &ABMembersController$sectionIndexTitlesForTableView, "orig$");
	
	MSHookMessage(objc_getClass("UITextField"), @selector(setText:), &setText, "orig$");
	MSHookMessage(objc_getClass("UITextViewLegacy"), @selector(setText:), &setText, "orig$");
	MSHookMessage(objc_getClass("UILabel"), @selector(setText:), &setText, "orig$");
	MSHookMessage(objc_getClass("UITableViewCell"), @selector(setText:), &setText, "orig$");
	
	MSHookMessage(objc_getClass("UITextField"), @selector(setTextAlignment:), &setTextAlignment, "orig$");
	
	MSHookMessage(objc_getClass("UITextViewLegacy"), @selector(setContentToHTMLString:), &setContentToHTMLString, "orig$");
	MSHookMessage(objc_getClass("UITextViewLegacy"), @selector(styleString), &styleString, "orig$");
	
	/*Class cls = objc_getClass("UITextViewLegacy");
	unsigned int methodCount, i;
	Method * methods = class_copyMethodList(cls, &methodCount);
	for(i=0;i<methodCount;i++){
		char *methodType = method_copyReturnType(methods[i]);
		const char *methodName = sel_getName(method_getName(methods[i]));
		NSLog(@"%s %s", methodType, methodName);
		free(methodType);
	}*/
	
	/*Class cls = objc_getClass("UITextViewLegacy");
	unsigned int ivarCount, i;
	Ivar * ivars = class_copyIvarList(cls, &ivarCount);
	for(i=0;i<ivarCount;i++){
		NSLog(@"%s %s", ivar_getTypeEncoding(ivars[i]), ivar_getName(ivars[i]));
	}*/
	
	//Instead of ABModelRecord to support Cyntact in search
	MSHookMessage(objc_getClass("ABMemberCell"), @selector(drawContentInRect:selected:), &ABMemberCell$drawContentInRectSelected, "orig$");
	// Wait for saurik
	//MSHookMessage(objc_getClass("ABMemberCell"), @selector(initWithSize:member:), &ABMemberCell$initWithSizeMember, "orig$");
	/***************************************************************************************************************************/
}
